/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.mzk.oai.metadataRepository.repository;

import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Martin Rehanek, Martin.Rehanek@gmail.com
 */
public class Identifier {

    private static final String ID_PREFIX = "oai:aleph.mzk.cz:";
    private static final String ID_PREFIX_ENCODED = "oai%3Aaleph.mzk.cz%3A";
    private static final String BASE_SYSNO_SEPARATOR = "-";
    private static final String BASE_SYSNO_SEPARATOR_ENCODED = "-";
    private static final int SYSNO_LENGTH = 9;
    private AlephBase base;
    private String sysno;
    private String id = null; //id as String constructed from prefix, base, sysno

    public static Identifier parseFileEncodedString(String filename) {
        return parseUrlEncodedString(filename);
    }

    public static Identifier parseNotEncodedString(String idStr) {
        String prefix = idStr.substring(0, ID_PREFIX.length());
        if (!ID_PREFIX.equals(prefix)) {
            throw new IllegalArgumentException("incorrect prefix");
        }
        String unPrefixed = idStr.substring(ID_PREFIX.length());
        String[] parts = unPrefixed.split(BASE_SYSNO_SEPARATOR);
        if (parts.length != 2) {
            throw new IllegalArgumentException();
        }
        AlephBase base = AlephBase.valueOf(parts[0]);
        String sysno = parts[1];
        return new Identifier(base, sysno);
    }

    public static Identifier parseUrlEncodedString(String idStr) {
        String prefix = idStr.substring(0, ID_PREFIX_ENCODED.length());
        if (!ID_PREFIX_ENCODED.equals(prefix)) {
            throw new IllegalArgumentException("incorrect prefix");
        }
        String unPrefixed = idStr.substring(ID_PREFIX_ENCODED.length());
        String[] parts = unPrefixed.split(BASE_SYSNO_SEPARATOR_ENCODED);
        if (parts.length != 2) {
            throw new IllegalArgumentException();
        }
        AlephBase base = AlephBase.valueOf(parts[0]);
        String sysno = parts[1];
        return new Identifier(base, sysno);
    }

    public Identifier(AlephBase base, String sysno) {
        this.base = base;
        this.sysno = normalizeSysno(sysno);
    }

    private String normalizeSysno(String sysno) {
        checkIsNumber(sysno);
        if (sysno.length() == SYSNO_LENGTH) {
            return sysno;
        } else {
            return addZeroesPrefix(sysno);
        }
    }

    private String addZeroesPrefix(String sysno) {
        if (sysno.length() > 9) {
            throw new IllegalArgumentException();
        }
        int zeroesToAdd = SYSNO_LENGTH - sysno.length();
        return addZeroesPrefix(zeroesToAdd, sysno);
    }

    private String addZeroesPrefix(int zeroesToAdd, String sysno) {
        StringBuilder result = new StringBuilder(zeroesToAdd + sysno.length());
        char zero = '0';
        for (int i = 0; i < zeroesToAdd; i++) {
            result.append(zero);
        }
        result.append(sysno);
        return result.toString();
    }

    @Override
    public String toString() {
        if (id != null) {
            return id;
        } else {
            StringBuilder builder = new StringBuilder();
            builder.append(ID_PREFIX);
            builder.append(getBase().toString());
            builder.append(BASE_SYSNO_SEPARATOR);
            builder.append(sysno);//TODO: 001041863 nebo 1041863 ?
            return builder.toString();
        }
    }

    /**
     * @param id String tested for being valid identifier
     * @param alephBase base of the identifier
     * @return true if id is valid identifier within the base
     */
    public static boolean isIdentifier(String id, String alephBase) {
        if (id == null) {
            return false;
        }
        int from;
        int to;
        int prefixLength = ID_PREFIX.length();
        int baseLength = alephBase.length();
        int separatorLength = BASE_SYSNO_SEPARATOR.length();

        //length
        int length = prefixLength + baseLength + separatorLength + SYSNO_LENGTH;
        if (id.length() != length) {
            return false;
        }

        //"oai:aleph.mzk.cz:"
        from = 0;
        to = prefixLength;
        String prefixSubstr = id.substring(from, to);
        if (!prefixSubstr.equals(ID_PREFIX)) {
            return false;
        }

        //"MZK03"
        from = prefixLength;
        to = prefixLength + baseLength;
        String baseSubstr = id.substring(from, to);
        if (!baseSubstr.equals(alephBase)) {
            return false;
        }

        //"-"
        from = prefixLength + baseLength;
        to = prefixLength + baseLength + separatorLength;
        String separatorSubstr = id.substring(from, to);
        if (!separatorSubstr.equals(BASE_SYSNO_SEPARATOR)) {
            return false;
        }

        return true;
    }

    /**
     * Transforms id in form oai:mzk.cz:oai:aleph.mzk.cz:MZK03-000906761 into
     * oai%3Amzk.cz%3Aoai%3Aaleph.mzk.cz%3AMZK03-000906761
     * @param id
     * @return
     * @deprecated 
     */
    public static String idToFileName(String id) {
        StringBuilder builder = new StringBuilder(id.length());
        for (int i = 0; i < id.length(); i++) {
            char character = id.charAt(i);
            if (character == ':') {
                builder.append("%3A");
            } else {
                builder.append(character);
            }
        }
        builder.append(".xml");
        return builder.toString();
    }

    public String toFilename() {
        return ID_PREFIX_ENCODED + getBase().toString() + BASE_SYSNO_SEPARATOR + sysno + ".xml";
    }

    public static String filenameToId(String filename) {
        //implementation detail: Sun recommends to use String.split instead of StringTokenizer
        //in some situations StringTokenizer can be faster though
        String encodedSeparator = "%3A";
        String[] tokens = filename.split(encodedSeparator);
        StringBuilder builder = new StringBuilder();
        int i;
        for (i = 0; i < (tokens.length - 1); i++) { //last token contains ".xml"
            builder.append(tokens[i]);
            builder.append(':');
        }
        String[] lastTokens = tokens[i].split("\\.");
        builder.append(lastTokens[0]);
        return builder.toString();
    }

    private void checkIsNumber(String sysno) {
        try {
            Integer.parseInt(sysno);
        } catch (NumberFormatException e) {
            Logger.getLogger(Identifier.class.getName()).log(Level.SEVERE, id, e);
            throw new IllegalArgumentException(e);
        }
    }

    /**
     * @return the base
     */
    public AlephBase getBase() {
        return base;
    }

    public String getSysno() {
        return sysno;
    }
}
